package com.cloud.common.utils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class BaiduMapConvert {

    private static final Double[] LLBAND = {75d, 60d, 45d, 30d, 15d, 0d};
    private static final Double[][] LL2MC = {
            {-0.0015702102444, 111320.7020616939, 1704480524535203d,
                    -10338987376042340d, 26112667856603880d,
                    -35149669176653700d, 26595700718403920d,
                    -10725012454188240d, 1800819912950474d, 82.5},
            {0.0008277824516172526, 111320.7020463578, 647795574.6671607,
                    -4082003173.641316, 10774905663.51142, -15171875531.51559,
                    12053065338.62167, -5124939663.577472, 913311935.9512032,
                    67.5},
            {0.00337398766765, 111320.7020202162, 4481351.045890365,
                    -23393751.19931662, 79682215.47186455, -115964993.2797253,
                    97236711.15602145, -43661946.33752821, 8477230.501135234,
                    52.5},
            {0.00220636496208, 111320.7020209128, 51751.86112841131,
                    3796837.749470245, 992013.7397791013, -1221952.21711287,
                    1340652.697009075, -620943.6990984312, 144416.9293806241,
                    37.5},
            {-0.0003441963504368392, 111320.7020576856, 278.2353980772752,
                    2485758.690035394, 6070.750963243378, 54821.18345352118,
                    9540.606633304236, -2710.55326746645, 1405.483844121726,
                    22.5},
            {-0.0003218135878613132, 111320.7020701615, 0.00369383431289,
                    823725.6402795718, 0.46104986909093, 2351.343141331292,
                    1.58060784298199, 8.77738589078284, 0.37238884252424, 7.45}};

    /**
     * 经纬度转瓦片
     *
     * @param lng  经度
     * @param lat  纬度
     * @param zoom 层级
     * @return 瓦片左+瓦片下
     */
    public static List<Integer> getTile(double lng, double lat, int zoom) {
        int width = 256;// 地图div宽度
        int height = 256;// 地图div高度

        Map<String, Double> center = convertLL2MC(lng, lat);
        double cV = Math.pow(2, 18 - zoom);// 现在直接计算这个数,理论不变,代码变了,我们直接计算好了
        double unitSize = cV * 256;
        double longitudeUnits = center.get("x") / unitSize / 1.0;// center.lng是一个很大的数
        double latitudeUnits = center.get("y") / unitSize / 1.0;// 这两个不用解释了吧
        double tileSize = 256; // 这个直接给常量吧,要不得是mapType.k.Ob // mapType.getTileSize();
        double[] cP = new double[]{
                longitudeUnits,
                latitudeUnits,
                (center.get("x") - longitudeUnits * unitSize) / unitSize * tileSize,
                (center.get("y") - latitudeUnits * unitSize) / unitSize * tileSize
        };
        double left = (cP[0] - (width / 2d - cP[2]) / tileSize);
        double top = (cP[1] - (height / 2d - cP[3]) / tileSize);
        double right = (cP[0] + (width / 2d + cP[2]) / tileSize);
        double c0 = 0;
        double bottom = (cP[1] + (height / 2d + cP[3]) / tileSize) + c0;
        // 当有区间时取中间值对应地图中间
        if (right - left > 0) {
            left = right - Math.ceil((right - left) / 2);
        }
        if (bottom - top > 0) {
            top = bottom - Math.ceil((bottom - top) / 2);
        }
        List<Integer> list = new ArrayList<>();
        list.add((int) left);
        list.add((int) top);
        return list;
    }


    /**
     * 经纬度坐标转墨卡托坐标
     *
     * @param lng 经度
     * @param lat 纬度
     * @return 结果
     */
    public static Map<String, Double> convertLL2MC(Double lng, Double lat) {
        Double[] cE = null;
        lng = getLoop(lng, -180, 180);
        lat = getRange(lat, -74, 74);
        for (int i = 0; i < LLBAND.length; i++) {
            if (lat >= LLBAND[i]) {
                cE = LL2MC[i];
                break;
            }
        }
        if (cE != null) {
            for (int i = LLBAND.length - 1; i >= 0; i--) {
                if (lat <= -LLBAND[i]) {
                    cE = LL2MC[i];
                    break;
                }
            }
        }
        return converter(lng, lat, cE);
    }

    private static Map<String, Double> converter(Double x, Double y, Double[] cE) {
        Double xTemp = cE[0] + cE[1] * Math.abs(x);
        Double cC = Math.abs(y) / cE[9];
        Double yTemp = cE[2] + cE[3] * cC + cE[4] * cC * cC + cE[5] * cC * cC
                * cC + cE[6] * cC * cC * cC * cC + cE[7] * cC * cC * cC * cC
                * cC + cE[8] * cC * cC * cC * cC * cC * cC;
        xTemp *= (x < 0 ? -1 : 1);
        yTemp *= (y < 0 ? -1 : 1);
        Map<String, Double> location = new HashMap<String, Double>();
        location.put("x", xTemp);
        location.put("y", yTemp);
        return location;
    }

    private static Double getLoop(Double lng, Integer min, Integer max) {
        while (lng > max) {
            lng -= max - min;
        }
        while (lng < min) {
            lng += max - min;
        }
        return lng;
    }

    private static Double getRange(Double lat, Integer min, Integer max) {
        if (min != null) {
            lat = Math.max(lat, min);
        }
        if (max != null) {
            lat = Math.min(lat, max);
        }
        return lat;
    }
}
